/*
* Copyright 2016 Substance Mobile
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.animbus.music.media.stable;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaButtonReceiver;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import com.animbus.music.media.Library;
import com.animbus.music.media.objects.Song;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_PAUSE;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SEEK_TO;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_STOP;
import static android.support.v4.media.session.PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN;
import static android.support.v4.media.session.PlaybackStateCompat.STATE_BUFFERING;
import static android.support.v4.media.session.PlaybackStateCompat.STATE_NONE;
import static android.support.v4.media.session.PlaybackStateCompat.STATE_PAUSED;
import static android.support.v4.media.session.PlaybackStateCompat.STATE_PLAYING;
import static android.support.v4.media.session.PlaybackStateCompat.STATE_SKIPPING_TO_NEXT;
import static android.support.v4.media.session.PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS;
import static android.support.v4.media.session.PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM;
import static android.support.v4.media.session.PlaybackStateCompat.STATE_STOPPED;
/**
* WARNING: This class is extremely experimental. This file was created on 7/18/2015 by Adrian.
*/
public class MediaService extends Service implements PlaybackManager.OnChangedListener {
private final static String TAG = "MediaService:";
private final IBinder mBinder = new MusicBinder();
//The media session
MediaSessionCompat mSession;
//The playback state
PlaybackStateCompat mState;
AudioManager mAudioManager;
ComponentName mButtonReceiver;
PendingIntent mButtonReceivedIntent;
//The notification
private MediaNotification mNotification;
private PlaybackManager mPlayback;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onSongChanged(Song song) {
mSession.setMetadata(song.data);
}
@Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
}
public class MusicBinder extends Binder {
public MediaService getService(){
return MediaService.this;
}
}
public void stopService() {
stopSelf();
}
public void setAsForeground() {
startForeground(MediaNotification.NOTIFICATION_ID, mNotification.getNotification());
}
public void removeForeground(boolean removeNotification){
stopForeground(removeNotification);
}
public void setUp(){
mPlayback = PlaybackManager.from(this);
mNotification = new MediaNotification(this);
mPlayback.mNotification = mNotification;
mPlayback.registerListener(this);
mButtonReceiver = new ComponentName(getPackageName(), MediaButtonReceiver.class.getName());
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mButtonReceivedIntent = PendingIntent.getBroadcast(
this,
0,
new Intent(Intent.ACTION_MEDIA_BUTTON),
PendingIntent.FLAG_UPDATE_CURRENT
);
mSession = new MediaSessionCompat(this, TAG);
mSession.setPlaybackToLocal(AudioManager.STREAM_MUSIC);
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mSession.setCallback(new MediaSessionCallback());
setState(STATE_NONE);
mSession.setMediaButtonReceiver(mButtonReceivedIntent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
MediaButtonReceiver.handleIntent(mSession, intent);
return super.onStartCommand(intent, flags, startId);
}
public void updateMetadata(MediaMetadataCompat metadata){
mSession.setMetadata(metadata);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
// Service is being killed, so make sure we release our resources
// Always release the MediaSession to clean up resources
// and notify associated MediaController(s).
mSession.release();
}
public void updateQueue() {
mSession.setQueue(QueueManager.get().getCurrentQueueAsQueueItem());
}
public void setState(int state) {
mState = new PlaybackStateCompat.Builder()
.setActions(
ACTION_PLAY |
ACTION_PAUSE |
ACTION_PLAY_PAUSE |
ACTION_SKIP_TO_NEXT |
ACTION_SKIP_TO_PREVIOUS |
ACTION_STOP |
ACTION_PLAY_FROM_MEDIA_ID |
ACTION_PLAY_FROM_SEARCH |
ACTION_SKIP_TO_QUEUE_ITEM |
ACTION_SEEK_TO)
.setState(state, PLAYBACK_POSITION_UNKNOWN, 1.0f, SystemClock.elapsedRealtime())
.build();
mSession.setPlaybackState(mState);
mSession.setActive(state != PlaybackStateCompat.STATE_NONE && state != PlaybackStateCompat.STATE_STOPPED);
for (PlaybackManager.OnChangedListener l : mPlayback.listeners){
l.onPlaybackStateChanged(mState);
}
}
public class MediaSessionCallback extends MediaSessionCompat.Callback {
@Override
public void onPlay() {
Log.d(TAG, "Play");
mPlayback.resume();
setState(STATE_PLAYING);
}
@Override
public void onPause() {
Log.d(TAG, "Pause");
mPlayback.pause();
setState(STATE_PAUSED);
}
@Override
public void onSkipToNext() {
super.onSkipToNext();
mPlayback.playNext();
setState(STATE_SKIPPING_TO_NEXT);
}
@Override
public void onSkipToPrevious() {
super.onSkipToPrevious();
mPlayback.playPrev(true);
setState(STATE_SKIPPING_TO_PREVIOUS);
}
@Override
public void onSeekTo(long pos) {
mPlayback.seekTo((int) pos);
setState(STATE_BUFFERING);
}
@Override
public void onSkipToQueueItem(long id) {
super.onSkipToQueueItem(id);
mPlayback.play(Library.findSongById(id));
setState(STATE_SKIPPING_TO_QUEUE_ITEM);
}
public void onTogglePlay() {
if (mState.getState() == STATE_PLAYING) {
onPause();
} else {
onPlay();
}
}
@Override
public void onStop() {
super.onStop();
mPlayback.stop();
setState(STATE_STOPPED);
}
@Override
public boolean onMediaButtonEvent(Intent mediaButtonEvent) {
boolean handled = false;
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(mediaButtonEvent.getAction())) {
Log.d(TAG, "Headphones disconnected.");
if (mState.getState() == STATE_PLAYING) {
onPause();
handled = true;
}
}
return handled;
}
}
///////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////
public MediaSessionCompat getSession(){
return mSession;
}
public PlaybackStateCompat getStateObj(){
return mState;
}
public int getState(){
return mState.getState();
}
public MediaSessionCallback getCallback() {
return new MediaSessionCallback();
}
public PlaybackManager getPlaybackManager(){
return mPlayback;
}
}